<?php
require_once('functions.php');

//les classes de validateurs de champs
abstract class Validateur
{
	protected $erreur;

	public function getErreur()
	{
		return $this->erreur;
	}

	public abstract function valider($val);
}

class ValidateurRequis extends Validateur
{
	public function valider($val)
	{
		if(is_null($val) || strlen($val) == 0)
		{
			$this->erreur = "Requis";
			return false;
		}
		else
		{
			return true;
		}
	}
}

class ValidateurNumerique extends Validateur
{
	public function valider($val)
	{
		if(is_null($val) || !is_numeric($val))
		{
			$this->erreur = "Type numérique attendu";
			return false;
		}
		else
		{
			return true;
		}
	}
}

class ValidateurCodePostal extends Validateur
{
	public function valider($val)
	{
            if(is_null($val) || strlen($val) != 5 || !is_numeric($val))
            {
                $this->erreur = "le code postal doit avoir 5 chiffres";
                return false;
            }
            else
            {
                return true;
            }

	}
}

class ValidateurExtension extends Validateur
{
        private $extension = null;

        public function __construct($ext)
        {
            $this->extension = $ext;
        }

        public function valider($val)
	{
		if( $this->VerifierExtension($val) )
		{
			return true;
		}
		else
		{
			$this->erreur = "Type invalide";
		}
	}

	private function VerifierExtension($fichier)
	{
		$syntaxe='#^.+\.(' . $this->extension .')$#';
		if(preg_match($syntaxe,$fichier))
                    return true;
                else
                    return false;
	}
}

class ValidateurText extends Validateur
{
    private $taille = 0;

    public function __construct($taille)
    {
        $this->taille = $taille;
    }
    public function valider($val)
    {
        if(strlen($val) <= $this->taille)
        {
            return true;
        }
        else
        {
            $this->erreur = "Taille maximum = " . $this->taille;
        }
    }
}

class ValidateurMail extends Validateur
{
	public function valider($val)
	{
		if( $this->VerifierAdresseMail($val) )
		{
			return true;
		}
		else
		{
			$this->erreur = "Email invalide";
		}
	}

	private function VerifierAdresseMail($adresse)
	{
		$Syntaxe='#^[\w.-]+@[\w.-]+\.[a-zA-Z]{2,6}$#';
		if(preg_match($Syntaxe,$adresse))
		return true;
		else
		return false;
	}
}

class ValidateurDate extends Validateur
{
	public function valider($val)
	{
		if( $this->VerifierDate($val) )
		{
			return true;
		}
		else
		{
			$this->erreur = "Date invalide";
		}
	}

	private function VerifierDate($date)
	{
		$Syntaxe='#^\d{2}/\d{2}/\d{4}$#';
		if(preg_match($Syntaxe,$date))
		return true;
		else
		return false;
	}
}

class ValidateurHeure extends Validateur
{
	public function valider($val)
	{
		if( $this->VerifierHeure($val) )
		{
			return true;
		}
		else
		{
			$this->erreur = "Heure invalide";
		}
	}

	private function VerifierHeure($date)
	{
		$Syntaxe='#^(\d{2}):(\d{2})$#';
		if(preg_match($Syntaxe,$date, $out))
                {
                    //vérification de l'heure et des minutes
                    if(intval($out[0]) >= 0 && intval($out[0]) <= 23 && intval($out[1]) >= 0 && intval($out[1]) <= 59)
                        return true;
                    else
                        return false;
                }
		else
                    return false;
	}
}

//les classes de champs de formulaire
abstract class Champ
{
	protected $nom = null;
	protected $label = null;
	protected $valeur = null;
	protected $attributs = null;
	private $validateurs = array();
	private $erreurs = array();
	private $requis = false;
	public $isvalid = true;

	public function __construct($nom, $label, $requis = false)
	{
		if(strlen($nom) > 0)
		{
			$this->nom = $nom;
			$this->label = $label;
			$this->requis = $requis;
			if($requis == true)
			{
				$this->validateurs[] = new ValidateurRequis();
			}
		}
		else
		{
			die("Erreur nom du champ pour le formulaire requis");
		}
		$this->attributs = array(
			   'id' => null,
			   'class' => null);
	}

	public function setValeur($val)
	{
		$this->valeur = $val;
	}

	public function getValeur()
	{
		return $this->valeur;
	}

	public abstract function addAttribut($nom, $valeur);

	public abstract function render();

	public function addValidateur(Validateur $val)
	{
		$this->validateurs[] = $val;
	}

        protected function setErreur($mesg)
        {
            $this->isvalid = false;
            $this->erreurs[] = $mesg;
        }

	public function valider()
	{
		$this->isvalid = true;
		foreach($this->validateurs as $validateur)
		{
			if( $validateur->valider($this->valeur) == false)
			{
				$this->isvalid = false;
				$this->erreurs[] = $validateur->getErreur();
			}
		}
	}

	public function isValid()
	{
		return $this->isvalid;
	}

	public function getErreurs()
	{
		return $this->erreurs;
	}

        public function getLabel()
        {
            return $this->label;
        }

        public abstract function bind($val);

        public function __toString()
        {
            return $this->nom;
        }
}


class ChampUpload extends Champ
{
        private $uploadrep = null;
        private $linkrep = null;
        
	public function __construct($name, $label, $requis = false)
	{
		parent::__construct($name, $label, $requis);

		$this->attributs['size'] = null;
                $this->attributs['accept'] = null;
	}

        public function setUploadRep($rep)
        {
            $this->uploadrep = rtrim($rep, "/\\");
        }

        public function setLinkRep($rep)
        {
            $this->linkrep = rtrim($rep, "/\\");
        }

        public function bind($val=null)
        {
            //Modification image
            if($val != null)
            {
                $this->setValeur($val);
                return;
            }

            //Nouvelle image recupération du fichier temporaire et transfert dans le répertoire d'upload
            $val = null;
            try
            {
                if(is_uploaded_file($_FILES[$this->nom]['tmp_name']))
                {
                    $fich = str_replace(" ", "_", $_FILES[$this->nom]['name']);
                    $fich = strtolower($fich);

                    if(rename($_FILES[$this->nom]['tmp_name'], $this->uploadrep."/".$fich))
                    {
                        $this->setValeur($this->linkrep."/".$fich);
                    }
                    else
                    {
                        throw new Exception("Le fichier temporaire n'a pas pu être déplacé");
                    }
                }
                else
                {
                    throw new Exception("Le fichier n'a pas été uploadé");
                }
            }
            catch(Exception $exception)
            {
                $val = null;
                $this->setErreur($exception->getMessage());
            }
        }

	public function addAttribut($nom, $valeur)
	{
		if(array_key_exists($nom, $this->attributs))
		{
			$this->attributs[$nom] = $valeur;
		}
		else
		{
			die("Attribut inexistant pour le champs de formulaire: " . $nom);
		}
	}

	public function render()
	{
                $str = '<div class="field field-upload"><label for="%s">%s</label>'."\n".'<input type="file" name="%s" %s/>%s</div>';
		$strattributs = "";
                $strvaleur = "";

                if($this->getValeur() != null)
                {
                    $strvaleur = "\n" + '<span class="legend">' . $this->getValeur() . "</span>";
                }
                
		foreach($this->attributs as $key => $val)
		{
			if(!is_null($val))
			{
				$strattributs .= ' ' . $key . '="' . $val .'"';
			}
		}
		$out = sprintf($str, $this->nom, $this->label, $this->nom, $strattributs, $strvaleur);
		return $out;
	}
}

class ChampText extends Champ
{
	public function __construct($name, $label, $requis = false)
	{
		parent::__construct($name, $label, $requis);

		$this->attributs['size'] = null;
		$this->attributs['maxlength'] = null;
		$this->attributs['value'] = null;
	}

        public function bind($val)
        {
            $this->setValeur($val);
            $this->addAttribut('value', html($val));
        }

	public function addAttribut($nom, $valeur)
	{
		if(array_key_exists($nom, $this->attributs))
		{
			$this->attributs[$nom] = $valeur;
		}
		else
		{
			die("Attribut inexistant pour le champs de formulaire: " . $nom);
		}
	}

	public function render()
	{
		$str = '<div class="field field-text"><label for="%s">%s</label>'."\n".'<input type="text" name="%s" %s/></div>';
		$strattributs = "";
		foreach($this->attributs as $key => $val)
		{
			if(!is_null($val))
			{
				$strattributs .= ' ' . $key . '="' . $val .'"';
			}
		}
		$out = sprintf($str, $this->nom, $this->label, $this->nom, $strattributs);
		return $out;
	}
}

class ChampDate extends ChampText
{
	public function __construct($name, $label, $requis = false)
	{
		parent::__construct($name, $label, $requis);

		$this->addAttribut('class', 'datetime');
                $this->addValidateur(new ValidateurDate());
	}
}

class ChampHeure extends ChampText
{
    public function __construct($name, $label, $requis = false)
    {
        parent::__construct($name, $label, $requis);

        $this->addAttribut('class', 'time');
        $this->addValidateur(new ValidateurHeure());
    }
}

class ChampPassword extends Champ
{
	public function __construct($name, $label, $requis = true)
	{
		parent::__construct($name, $label, $requis);

		$this->attributs['size'] = null;
		$this->attributs['maxlength'] = null;
		$this->attributs['value'] = null;
	}

        public function bind($val)
        {
            $this->setValeur($val);
            $this->addAttribut('value', $val);
        }

	public function addAttribut($nom, $valeur)
	{
		if(array_key_exists($nom, $this->attributs))
		{
			$this->attributs[$nom] = $valeur;
		}
		else
		{
			die("Attribut inexistant pour le champs de formulaire: " . $nom);
		}
	}

	public function render()
	{
		$str = '<div class="field field-password"><label for="%s">%s</label>'."\n".'<input type="password" name="%s" %s/></div>';
		$strattributs = "";
		foreach($this->attributs as $key => $val)
		{
			if(!is_null($val))
			{
				$strattributs .= ' ' . $key . '="' . $val .'"';
			}
		}
		$out = sprintf($str, $this->nom, $this->label, $this->nom, $strattributs);
		return $out;
	}
}

class ChampHidden extends Champ
{
	public function __construct($name, $label, $requis = true)
	{
		parent::__construct($name, $label, $requis);

		$this->attributs['value'] = null;
	}

        public function bind($val)
        {
            $this->setValeur($val);
            $this->addAttribut('value', $val);
        }

	public function addAttribut($nom, $valeur)
	{
		if(array_key_exists($nom, $this->attributs))
		{
			$this->attributs[$nom] = $valeur;
		}
		else
		{
			die("Attribut inexistant pour le champs de formulaire: " . $nom);
		}
	}

	public function render()
	{
		$str = '<div class="field"><input type="hidden" name="%s" %s/></div>';
		$strattributs = "";
		foreach($this->attributs as $key => $val)
		{
			if(!is_null($val))
			{
				$strattributs .= ' ' . $key . '="' . $val .'"';
			}
		}
		$out = sprintf($str, $this->nom, $strattributs);
		return $out;
	}
}

class ChampList extends Champ
{
	private $selected = null;
	private $multiple = false;
	private $options = array();
	
	public function __construct($name, $label, $multiple = false, $requis = true)
	{
		parent::__construct($name, $label, $requis);

		$this->attributs['value'] = null;
		$this->attributs['size'] = null;
	}

        public function bind($val)
        {
            $this->setValeur($val);
            $this->setSelected($val);
        }
	
	public function setSelected($index)
	{
            $i = 0;
            foreach($this->options as $option)
            {
                if($index == $option['value'])
                {
                    $this->selected = $i;
                }
                $i++;
            }
	}
	
	public function setMultiple($b)
	{
		if($b == true)
		{
			$this->multiple = true;
		}
		else
		{
			$this->multiple = false;
		}
	}
	
	public function addOption($name, $label, $value)
	{
		$opt = array('name'=> $name, 'label' => $label, 'value' => $value);
		$this->options[] = $opt;
	}
	
	public function addAttribut($nom, $valeur)
	{
		if(array_key_exists($nom, $this->attributs))
		{
			$this->attributs[$nom] = $valeur;
		}
		else
		{
			die("Attribut inexistant pour le champs de formulaire: " . $nom);
		}
	}
	
	public function render()
	{
		$str = '<div class="field field-list"><label for="%s">%s</label>'."\n".'<select name="%s" %s %s>'."\n";
		$strattributs = "";
		foreach($this->attributs as $key => $val)
		{
			if(!is_null($val))
			{
				$strattributs .= ' ' . $key . '="' . $val .'"';
			}
		}
		$out = sprintf($str, $this->nom, $this->label, $this->nom, $this->multiple == true?'multiple':'',$strattributs);
		$i = 0;
		
		foreach($this->options as $option)
		{
			$stropt = '<option %s value="%s" %s>%s</option>' . "\n";
			$out .= sprintf($stropt, is_null($option['name'])?'':'name="'. $option['name'] .'"', $option['value'], $i == $this->selected?'selected':'', $option['label']);
			$i++;
		}
		
		$out .= '</select></div>';
		return $out;
	}
}

class ChampTextArea extends Champ
{
	public function __construct($name, $label, $requis = false)
	{
		parent::__construct($name, $label, $requis);

		$this->attributs['size'] = null;
		$this->attributs['cols'] = null;
		$this->attributs['rows'] = null;
                $this->attributs['class'] = null;
                $this->attributs['id'] = null;
                $this->attributs['style'] = null;
	}

        public function bind($val)
        {
            $this->setValeur($val);
        }

	public function addAttribut($nom, $valeur)
	{
		if(array_key_exists($nom, $this->attributs))
		{
			$this->attributs[$nom] = $valeur;
		}
		else
		{
			die("Attribut inexistant pour le champs de formulaire: " . $nom);
		}
	}

	public function render()
	{
		$str = '<div class="field field-textarea"><label for="%s">%s</label><br/>'."\n".'<textarea name="%s" %s>%s</textarea></div>';
		$strattributs = "";
		foreach($this->attributs as $key => $val)
		{
			if(!is_null($val))
			{
				$strattributs .= ' ' . $key . '="' . $val .'"';
			}
		}
		$out = sprintf($str, $this->nom, $this->label, $this->nom, $strattributs, $this->valeur);
		return $out;
	}
}

class ChampButton extends Champ
{
	public function __construct($name, $label, $requis = false)
	{
		parent::__construct($name, $label);
		
	}

        public function bind($val)
        {
            $this->setValeur($val);
        }
	
	public function addAttribut($nom, $valeur)
	{
		if(array_key_exists($nom, $this->attributs))
		{
			$this->attributs[$nom] = $valeur;
		}
		else
		{
			die("Attribut inexistant pour le champs de formulaire: " . $nom);
		}
	}
	
	public function render()
	{
		$str = '<div class="field field-button"><input name="%s" type="button" value="%s" %s /></div>';
		$strattributs = "";
		foreach($this->attributs as $key => $val)
		{
			if(!is_null($val))
			{
				$strattributs .= ' ' . $key . '="' . $val .'"';
			}
		}
		$out = sprintf($str, $this->nom, $this->label, $strattributs);
		return $out;
	}
}

class ChampSubmit extends ChampButton
{
	public function __construct($name, $label, $requis = false)
	{
		parent::__construct($name, $label, $requis);
	}

        public function bind($val)
        {
            $this->setValeur($val);
        }
	
	public function render()
	{
		$str = '<div class="field field-submit"><input type="submit" value="%s" %s /></div>';
		$strattributs = "";
		foreach($this->attributs as $key => $val)
		{
			if(!is_null($val))
			{
				$strattributs .= ' ' . $key . '="' . $val .'"';
			}
		}
		$out = sprintf($str, $this->label, $strattributs);
		return $out;
	}
}

class ChampReset extends ChampButton
{
	public function __construct($name, $label, $requis = false)
	{
		parent::__construct($name, $label, $requis);
	}

        public function bind($val)
        {
            $this->setValeur($val);
        }

	public function render()
	{
		$str = '<div class="field field-reset"><input type="reset" value="%s" %s /></div>';
		$strattributs = "";
		foreach($this->attributs as $key => $val)
		{
			if(!is_null($val))
			{
				$strattributs .= ' ' . $key . '="' . $val .'"';
			}
		}
		$out = sprintf($str, $this->label, $strattributs);
		return $out;
	}
}

class ChampRadio extends Champ
{
	private $checked = false;
	
	public function __construct($name, $label, $checked = false, $requis = false)
	{
		parent::__construct($name, $label);
		$this->checked = $checked;
		
		$this->attributs['value'] = null;
	}
	
	public function setChecked($val)
	{
		if($val == true)
		{
			$this->checked = true;
		}
		else
		{
			$this->checked = false;
		}
	}

        public function bind($val)
        {
            $this->setValeur($val);
            $this->setChecked($val);
        }
	
	public function addAttribut($nom, $valeur)
	{
		if(array_key_exists($nom, $this->attributs))
		{
			$this->attributs[$nom] = $valeur;
		}
		else
		{
			die("Attribut inexistant pour le champs de formulaire: " . $nom);
		}
	}
	
	public function render()
	{
		$str = '<div class="field field-radio"><input type="radio" name="%s" %s %s/>%s</div>';
		$strattributs = "";
		foreach($this->attributs as $key => $val)
		{
			if(!is_null($val))
			{
				$strattributs .= ' ' . $key . '="' . $val .'"';
			}
		}
		$out = sprintf($str, $this->nom, $this->checked == true?'checked':'', $strattributs, $this->label);
		return $out;
	}
}

class ChampCheckBox extends Champ
{
	private $checked = false;
	
	public function __construct($name, $label, $valeur = "", $checked = false, $requis = false)
	{
		parent::__construct($name, $label);
		$this->checked = $checked;

                //ajout l'attribut value pour la cb
                $this->attributs['value'] = null;

                //défini la valeur vide par défaut
                $this->setValeur("");

                //valeur de la checkbox dans attribut value
                if(strlen($valeur) > 0)
                {
                    $this->addAttribut("value", $valeur);
                }
	}
	
	public function setChecked($val)
	{
		if($val == true)
		{
			$this->checked = true;
		}
		else
		{
			$this->checked = false;
		}
	}

        public function getChecked()
        {
            return $this->checked;
        }

        public function bind($val)
        {
            //si la valeur à lier est égale à la valeur de l'attribut value de la cb
            if($val == $this->attributs["value"])
            {
                //la cb est cochée
                $this->setChecked(true);
                //la valeur de la cb est la valeur postée
                $this->setValeur($val);
            }
            else
            {
                //la cb n'est pas cochée
                $this->setChecked(false);
                //la valeur de la cb est vide
                $this->setValeur("");
            }
        }
	
	public function addAttribut($nom, $valeur)
	{
		if(array_key_exists($nom, $this->attributs))
		{
			$this->attributs[$nom] = $valeur;
		}
		else
		{
			die("Attribut inexistant pour le champs de formulaire: " . $nom);
		}
	}
	
	public function render()
	{
		$str = '<div class="field field-checkbox"><input type="checkbox" name="%s" %s %s />%s</div>';
		$strattributs = "";
		foreach($this->attributs as $key => $val)
		{
			if(!is_null($val))
			{
				$strattributs .= ' ' . $key . '="' . $val .'"';
			}
		}
		$out = sprintf($str, $this->nom, $this->checked == true?'checked = "checked"':'', $strattributs, $this->label);
		return $out;
	}
}

class ChampFieldSet extends Champ
{
    private $champs = array();
    private $erreur = array();
    public $isvalid = true;

    public function __construct($name, $label)
    {
        parent::__construct($name, $label);
    }

    public function addChamp(Champ $champ)
    {
        $this->champs[] = $champ;
    }

    public function getChamps()
    {
        return $this->champs;
    }

    public function bind($val)
    {
        $this->setValeur($val);
    }

    public function bindChamps($method)
    {
        foreach($this->champs as $champ)
        {
            //traite le champs de type upload
            if(get_class($champ) == 'ChampUpload')
            {
                $champ->bind();
            }
            else
            {
                if($method == 'GET')
                {
                    if(isset($_GET[$champ->__toString()]) && !is_null($_GET[$champ->__toString()]))
                    {
                        $champ->bind(html($_GET[$champ->__toString()]));
                    }
                }
                else
                {
                    //pour les checkboxs attention si pas cochée pas de valeur dans le POST du form
                    if(get_class($champ) == 'ChampCheckBox')
                    {
                        //mise à vide de la valeur
                        //cette valeur sera changée si la cb est définie dans le post
                        //avec sa valeur
                        $champ->bind("");
                    }
                    if(isset($_POST[$champ->__toString()]) && !is_null($_POST[$champ->__toString()]))
                    {
                        $champ->bind(html($_POST[$champ->__toString()]));
                    }
                }
            }
        }
    }

    public function addAttribut($nom, $valeur)
    {
            if(array_key_exists($nom, $this->attributs))
            {
                    $this->attributs[$nom] = $valeur;
            }
            else
            {
                    die("Attribut inexistant pour le champs de formulaire: " . $nom);
            }
    }

    public function valider()
    {
        $this->isvalid = true;

        foreach($this->champs as $champ)
        {
            $champ->valider();
            if($champ->isvalid == false)
            {
                $this->isvalid = false;
                foreach($champ->getErreurs() as $key => $champerreur)
                {
                    $this->erreur[] = $champ->getLabel() . ' : ' . $champerreur;
                }
            }
        }

        return $this->isvalid;
    }

    public function getErreurs()
    {
            return $this->erreur;
    }

    public function render()
    {
            $str = "<fieldset>\n<legend>%s</legend>\n%s</fieldset>";
            $strchamps = "";
            foreach($this->champs as $key => $champ)
            {
                    if(!is_null($champ))
                    {
                            $strchamps .= $champ->render() . "\n";
                    }
            }
            $out = sprintf($str, $this->label, $strchamps);
            return $out;
    }
}

class Formulaire
{
	private $champs = array();
	private $isvalid = true;
	private $action = null;
	private $name = null;
	private $method = 'GET';
	protected $attributs = null;
	
	public function __construct($name, $action, $method = 'GET')
	{
		if(strlen($name) > 0 && strlen($action) > 0)
		{
			$this->name = $name;
			$this->action = $action;
			$this->method = $method;
		}
		else
		{
			die("Erreur le nom ou la cible du formulaire ne sont pas définis");
		}
		
		$this->attributs = array(
			'id' => null,
			'class' => null,
                        'enctype' => null
		);
	}
	
	public function addAttribut($nom, $valeur)
	{
		if(array_key_exists($nom, $this->attributs))
		{
			$this->attributs[$nom] = $valeur;
		}
		else
		{
			die("Attribut inexistant pour le champs de formulaire: " . $nom);
		}
	}
	
	public function addChamp(Champ $champ)
	{
		$this->champs[] = $champ;
	}
	
	public function getChamps()
	{
		return $this->champs;
	}

        public function valider()
        {
            $this->isvalid = true;

            foreach($this->champs as $champ)
            {
                $champ->valider();
                if($champ->isvalid == false)
                {
                    $this->isvalid = false;
                }
            }

            return $this->isvalid;
        }

        public function bind()
        {
            foreach($this->champs as $champ)
            {
                //traite le champs de type upload
                if(get_class($champ) == 'ChampUpload')
                {
                    $champ->bind();
                }
                else
                {
                    if(get_class($champ) == 'ChampFieldSet')
                    {
                        $champ->bindChamps($this->method);
                    }
                    else
                    {
                        //pour les checkboxs attention si pas cochée pas de valeur dans le POST du form
                        if(get_class($champ) == 'ChampCheckBox')
                        {
                            //mise à vide de la valeur
                            //cette valeur sera changée si la cb est définie dans le post
                            //avec sa valeur
                            $champ->bind("");
                        }
                        if($this->method == 'GET')
                        {
                            if(isset($_GET[$champ->__toString()]) && !is_null($_GET[$champ->__toString()]))
                            {
                                $champ->bind(html($_GET[$champ->__toString()]));
                            }
                        }
                        else
                        {
                            if(isset($_POST[$champ->__toString()]) && !is_null($_POST[$champ->__toString()]))
                            {
                                $champ->bind(html($_POST[$champ->__toString()]));
                            }
                        }
                    }
                }
            }
        }
	
	public function render()
	{
                //champs hidden pour xss
                $champid = new ChampHidden('formid', 'formid');
                $champid->addAttribut('value', 'form' . rand(100000, 999999));
                $this->addChamp($champid);

		$str = '<form name="%s" action="%s" method="%s" %s>' . "\n";
		$strattributs = "";
		foreach($this->attributs as $key => $val)
		{
			if(!is_null($val))
			{
				$strattributs .= ' ' . $key . '="' . $val .'"';
			}
		}
		
		$out = sprintf($str, $this->name, $this->action, $this->method, $strattributs);
		
		foreach($this->champs as $champ)
		{
			$out .= $champ->render() . "\n";
		}
		
		$out .= '</form>';
		return $out;
	}
}